﻿namespace MapleRock.P2PEmulator.Core
{
  using System;
  using System.Collections.Generic;
  using System.Diagnostics;
  using System.Linq;
  using System.Text;

  public sealed class DataTransportEmulator
  {
    private List<Message> scheduledMessages = new List<Message>();

    public void ScheduleMessage(Message message)
    {
      Debug.Assert(!scheduledMessages.Contains(message), "!scheduledMessages.Contains(message)");

      message.RoutingState.LatencyPassed = 0;

      scheduledMessages.Add(message);
    }

    public void EmulateDataTransfer(int periodMs, NetworkState state)
    {
      var deliveredMessages = new List<Message>();

      foreach (var message in this.scheduledMessages)
      {
        var fromPeer = state.Peers[message.From];
        var toPeer = state.Peers[message.To];

        var estimatedLatency = LatencyTable.GetLatency(
          Geometry.EvaluateDistance(fromPeer.GeoPosition, toPeer.GeoPosition));

        message.RoutingState.LatencyPassed += periodMs;
        if (message.RoutingState.LatencyPassed >= estimatedLatency)
        {
          if (!DataTransportEmulator.IsDatagrammLost(state))
          {
            toPeer.Incoming.Enqueue(message);

            if (!state.ActivePeers.Contains(toPeer))
            {
              state.ActivePeers.Add(toPeer);
            }
          }

          deliveredMessages.Add(message);
        }
      }

      foreach (var message in deliveredMessages)
      {
        this.scheduledMessages.Remove(message);
      }
    }

    public void ResetState()
    {
      this.scheduledMessages.Clear();
    }

    private static bool IsDatagrammLost(NetworkState state)
    {
      Debug.Assert(state.UdpDatagrammLossProbability >= 0, "state.UdpDatagrammLossProbability >= 0");
      Debug.Assert(state.UdpDatagrammLossProbability <= 0.05, "state.UdpDatagrammLossProbability <= 0.05");

      return (state.Rand.NextDouble() < state.UdpDatagrammLossProbability);
    }
  }
}
